; 这个是在于渊老师代码的基础上阉割以后可以编译成小于512字节的程序
; 旨在让程序实现对从ring0到ring3的转移并在ring3中通过调用门，调用ring0的代码。
; 这个程序不好精简，需要动些脑子才能精简下来。
; 基本思路是把gdt项都提前计算好，这样可以省下很多对gdt项赋值基址的代码。
; 这样gdt就变成了全是魔数，看起来有点奇怪，不用奇怪，早期的linux代码也是这么干的，这就是跟Linus学习的^^
; 不过我并没有把这个程序调好，虽然它正常显示了In PM now，但是在后面的retf的时候，报了错。
; 我试图解决这个错误，最终没搞定，往后再想办法收拾它，先提交吧。
; 另外pmtest5.asm就不准备加boot版了，加上跳回实模式的代码，512字节不够用。

; ==========================================
; pmtest5c.asm
; 编译方法：nasm pmtest5c.asm -o pmtest5c.com
; ==========================================

%include	"pm.inc"	; 常量, 宏, 以及一些说明

%ifdef DOS_DEBUG
org 0100h
%else
org 07c00h
%endif
	
jmp	LABEL_BEGIN

[SECTION .gdt]
; GDT
;                                         段基址,       段界限     , 属性
LABEL_GDT:         Descriptor       0,                 0, 0     	; 空描述符
LABEL_DESC_CODE32: dq 0x004098007d2c00ee	; 非一致代码段, 32
LABEL_DESC_DATA:  dq  0x000092007c600040	; Data
LABEL_DESC_STACK: dq  0x004093007c80001f; Stack, 32 位
LABEL_DESC_VIDEO: Descriptor 0B8000h,            0ffffh, DA_DRW + DA_DPL3	; 显存首地址
LABEL_DESC_CODE_DEST: dq  0x004098007d7400ee
LABEL_DESC_CODE_RING3: dq 0x0040f8007da000ee
LABEL_DESC_STACK3: dq     0x000089007ca00068
LABEL_DESC_TSS: dq 0x000089007cc00068
LABEL_CALL_GATE_TEST: Gate SelectorCodeDest, 0, 0, DA_386CGate + DA_DPL3
; GDT 结束


GdtLen		equ	$ - LABEL_GDT	; GDT长度
GdtPtr		dw	GdtLen - 1	; GDT界限
		dd	0x00007c04		; GDT基地址

; GDT 选择子
SelectorCode32		equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorData		equ	LABEL_DESC_DATA		- LABEL_GDT
SelectorStack		equ	LABEL_DESC_STACK	- LABEL_GDT
SelectorVideo		equ	LABEL_DESC_VIDEO	- LABEL_GDT
SelectorCodeDest equ LABEL_DESC_CODE_DEST - LABEL_GDT
SelectorCodeRing3 equ LABEL_DESC_CODE_RING3 - LABEL_GDT + SA_RPL3
SelectorStack3 equ LABEL_DESC_STACK3 - LABEL_GDT + SA_RPL3
SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT + SA_RPL3
SelectorTSS equ LABEL_DESC_TSS - LABEL_GDT
; END of [SECTION .gdt]

[SECTION .data1]	 ; 数据段
ALIGN	32
[BITS	32]
LABEL_DATA:
; 字符串
PMMessage:		db	"In PM now. ^-^", 0	; 进入保护模式后显示此字符串
OffsetPMMessage		equ	PMMessage - $$
DataLen			equ	$ - LABEL_DATA
; END of [SECTION .data1]


; 全局堆栈段
[SECTION .gs]
ALIGN	32
[BITS	32]
LABEL_STACK:
	times 32 db 0

TopOfStack	equ	$ - LABEL_STACK - 1
; END of [SECTION .gs]

; 堆栈段ring3
[SECTION .s3]
ALIGN 32
[BITS 32]
LABEL_STACK3:
	times 32 db 0
TopOfStack3 equ $ - LABEL_STACK3 - 1
; END of [SECTION .s3]

; TSS
[SECTION .tss]
ALIGN 32
[BITS 32]
LABEL_TSS:
	DD 0
	DD TopOfStack
	DD SelectorStack
	DD	0			; 1 级堆栈
	DD	0			; 
	DD	0			; 2 级堆栈
	DD	0			; 
	DD	0			; CR3
	DD	0			; EIP
	DD	0			; EFLAGS
	DD	0			; EAX
	DD	0			; ECX
	DD	0			; EDX
	DD	0			; EBX
	DD	0			; ESP
	DD	0			; EBP
	DD	0			; ESI
	DD	0			; EDI
	DD	0			; ES
	DD	0			; CS
	DD	0			; SS
	DD	0			; DS
	DD	0			; FS
	DD	0			; GS
	DD	0			; LDT
	DW	0			; 调试陷阱标志
	DW $ - LABEL_TSS + 2
	DB 0ffh
	
TSSLen equ $ - LABEL_TSS
; END of [SECTION .tss]

[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS	32]

LABEL_SEG_CODE32:
	mov	ax, SelectorData
	mov	ds, ax			; 数据段选择子
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子

	mov	ax, SelectorStack
	mov	ss, ax			; 堆栈段选择子

	mov	esp, TopOfStack


	; 下面显示一个字符串
	mov	ah, 0Ch			; 0000: 黑底    1100: 红字
	xor	esi, esi
	xor	edi, edi
	mov	esi, OffsetPMMessage	; 源数据偏移
	mov	edi, (80 * 10 + 0) * 2	; 目的数据偏移。屏幕第 10 行, 第 0 列。
	cld
.1:
	lodsb
	test	al, al
	jz	.2
	mov	[gs:edi], ax
	add	edi, 2
	jmp	.1
.2:	; 显示完毕
	
	mov ax, SelectorTSS
	ltr ax
	
	push SelectorStack3
	push TopOfStack3
	push SelectorCodeRing3
	push 0
	retf
	
	ud2
SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]


[SECTION .sdest]
[BITS 32]
LABEL_SEG_CODE_DEST:
	mov ax, SelectorVideo
	mov gs, ax
	
	mov edi, (80 * 12 + 0) * 2
	mov ah, 0Ch
	mov al, 'G'
	mov [gs:edi], ax
	
	retf
SegCodeDestLen equ $ - LABEL_SEG_CODE_DEST

[SECTION .ring3]
ALIGN 32
[BITS 32]
LABEL_CODE_RING3:
	mov ax, SelectorVideo
	mov gs, ax
	
	mov edi, (80 * 14 + 0) * 2
	mov ah, 0Ch
	mov al, '3'
	mov [gs:edi], ax
	
	call	SelectorCallGateTest:0
	jmp $
SegCodeRing3Len equ $ - LABEL_CODE_RING3


[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, 0100h

	lgdt	[GdtPtr]

	; 关中断
	cli

	; 打开地址线A20
	in	al, 92h
	or	al, 00000010b
	out	92h, al

	; 准备切换到保护模式
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax

	; 真正进入保护模式
	jmp	dword SelectorCode32:0	

; END of [SECTION .s16]
